package org.budo.support.lang.util;

import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Set;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.Manifest;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

/**
 * @author limingwei
 */
@Getter
@Setter
@Slf4j
public class JarInfo implements Serializable {
    private static final long serialVersionUID = -2294011778448732992L;

    private String groupId;

    private String artifactId;

    private String version;

    private String buildAt;

    public static JarInfo of(String name) {
        try {
            Enumeration<URL> enumeration = JarInfo.class.getClassLoader().getResources(name);
            List<URL> urls = Collections.list(enumeration);
            URL url = ListUtil.first(urls);
            if (null == url) {
                log.error("#26 resource " + name + " not found");
                return null;
            }

            String protocol = url.getProtocol();
            if ("jar".equals(protocol)) {
                return _jarInfo_2(url);
            }

            log.error("#37 protocol=" + protocol);
            return null;
        } catch (Throwable e) {
            log.error("#121 e=" + e, e);
            return null;
        }
    }

    public static JarInfo _jarInfo_2(URL url) {
        try {
            JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
            JarFile jarFile = jarURLConnection.getJarFile();

            // 遍历Jar包
            Enumeration<JarEntry> entries = jarFile.entries();
            while (entries.hasMoreElements()) {
                JarEntry jarEntry = (JarEntry) entries.nextElement();
                if (jarEntry.getName().endsWith("pom.properties")) {
                    InputStream inputStream = jarFile.getInputStream(jarEntry);
                    return _jarInfo_3(inputStream);
                }
            }

            // log.warn("#79 _jarInfo_2 no pom, url=" + url);
            return _jarInfoByName(jarFile);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

    private static JarInfo _jarInfoByName(JarFile jarFile) {
        try {
            Manifest manifest = jarFile.getManifest();
            if(null==manifest) {
                log.warn("#87 jarFile=" + jarFile.getName() + ", manifest=" + manifest);
                return null;
            }

            Attributes mainAttributes = manifest.getMainAttributes();

            String artifactId = mainAttributes.getValue("Implementation-Title");
            String version = mainAttributes.getValue("Implementation-Version");

            if (null == artifactId || null == version) {
                log.warn("#92 jarFile=" + jarFile.getName() + ", mainAttributes.values=" + mainAttributes.values());
                return null;
            }

            JarInfo jarInfo = new JarInfo();
            jarInfo.setArtifactId(artifactId);
            jarInfo.setVersion(version);
            return jarInfo;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private static JarInfo _jarInfo_3(InputStream inputStream) {
        JarInfo jarInfo = new JarInfo();

        Set<String> lines = IoUtil.bufferedReadLineToStringSet(inputStream);
        for (String line : lines) {
            if (line.startsWith("groupId=")) {
                jarInfo.setGroupId(line.substring(8));
            }
            if (line.startsWith("artifactId=")) {
                jarInfo.setArtifactId(line.substring(11));
            }
            if (line.startsWith("version=")) {
                jarInfo.setVersion(line.substring(8));
            }
            if (line.startsWith("#") && !line.startsWith("#Generated") && !line.startsWith("#Created")) {
                jarInfo.setBuildAt(line.substring(1));
            }
        }

        return jarInfo;
    }
}